from django.core.exceptions               import ImproperlyConfigured
from django.contrib                       import messages
from django.db.models.manager             import Manager
from django.db.models.query               import QuerySet
from django.forms                         import ModelForm
from django.forms.models                  import BaseInlineFormSet
from django.http                          import HttpResponseServerError
from django.utils.translation             import ugettext as _
from sitepal.sitemenu.actions.actions     import ACTION_SUBMIT_FORM,ACTION_CREATE_OBJECT
from sitepal.sitemenu.actions.object_view import ObjectView

class ObjectFormView(ObjectView):
    MODE_CREATE = 'create'
    MODE_UPDATE = 'update'

    class FormSetConf(object):
        def __init__(self,view,name,kwargs):
            assert isinstance(view,ObjectFormView)
            assert isinstance(name,basestring)
            self.view = view
            self.name = name
            if isinstance(kwargs,dict):
                self.formset           = kwargs['formset']
                self.queryset          = kwargs.get('queryset')
                self._apply_to_formset = kwargs.get('apply_to_formset')
            else:
                self.formset  = kwargs
                self.queryset = self._apply_to_formset = None
            assert issubclass(self.formset,BaseInlineFormSet)
            assert self.queryset is None or isinstance(self.queryset,(QuerySet,Manager))
            assert self._apply_to_formset is None or callable(self._apply_to_formset)
            assert self.formset.fk.rel.to == self.view._model_class

        @property
        def accessor(self):
            return self.formset.rel_name
        @property
        def model(self):
            return self.formset.model

        def apply_to_formset(self,request,instance,formset):
            if self._apply_to_formset:
                self._apply_to_formset(self,request,instance,formset)

    def __init__(self,form_class,post_save_redirect,cancel_save_redirect,mode,
                 apply_to_form=None,form_prefix=None,formsets=None,**kwargs):
        self._form_class           = form_class
        self._post_save_redirect   = post_save_redirect
        self._cancel_save_redirect = cancel_save_redirect
        self._apply_to_form        = apply_to_form
        self._form_prefix          = form_prefix
        self._formsets             = formsets
        self._mode                 = mode
        if not issubclass(form_class,ModelForm):
            raise ValueError,"Form class must be a subclass of 'ModelForm'"
        if self._mode not in (self.MODE_CREATE,self.MODE_UPDATE):
            raise ValueError,"Mode must be either '{0}' or '{1}'".format(self.MODE_CREATE,self.MODE_UPDATE)
        ObjectView.__init__(self,form_class.Meta.model,**kwargs)
        formsets = []
        if self._formsets:
            if not isinstance(self._formsets,dict):
                raise ValueError,"Form sets must be none or a dict"
            for name,formset in self._formsets.iteritems():
                formsets.append(self.FormSetConf(self,name,formset))
        self._formsets = formsets

    def get_object(self,request,**kwargs):
        from sitepal.sitemenu.actions import Http403
        
        if self._mode == self.MODE_CREATE:
            if not self.has_perm(ACTION_CREATE_OBJECT,request.user):
                raise Http403
            return self._model_class()
        else:
            return ObjectView.get_object(self,request,**kwargs)

    def get_context(self,request,obj,**kwargs):
        context                = ObjectView.get_context(self,request,obj,**kwargs)
        context['form_action'] = request.path
        return context

    def get_form(self,request,obj,**kwargs):
        from sitepal.sitemenu.actions import Http403
        
        if not self.has_perm(ACTION_SUBMIT_FORM,request.user,obj):
            raise Http403
        if callable(self._form_prefix):
            prefix = self._form_prefix(self,request,obj,**kwargs)
        else:
            prefix = self._form_prefix
        if request.method == 'POST':
            form = self._form_class(request.POST,request.FILES,instance=obj,prefix=prefix)
        elif request.method == 'GET':
            form = self._form_class(instance=obj,prefix=prefix)
        else:
            return HttpResponseServerError
        if self._apply_to_form:
            self._apply_to_form(self,request,form)
        return form

    def get_formsets(self,request,obj,**kwargs):
        #TODO: Make authorization check on formsets
        formsets = dict()
        for conf in self._formsets:
            args = {'prefix': conf.name,'instance':obj,'queryset':conf.queryset}
            if request.method == 'POST':
                formset = conf.formset(request.POST,request.FILES,**args)
            elif request.method == 'GET':
                formset = conf.formset(**args)
            else:
                return HttpResponseServerError
            conf.apply_to_formset(request,obj,formset)
            formsets[conf.name] = formset
        return formsets

    def handle_get(self,request,**kwargs):
        obj      = self.get_object(request,**kwargs)
        form     = self.get_form(request,obj,**kwargs)
        formsets = self.get_formsets(request,obj,**kwargs)
        context  = self.get_context(request,obj,form=form,**formsets)
        return self.response(request,context)

    def handle_post(self,request,**kwargs):
        if 'cancel' in request.POST:
            return self.cancel_save_redirect(request)
        obj      = self.get_object(request,**kwargs)
        form     = self.get_form(request,obj,**kwargs)
        formsets = self.get_formsets(request,obj,**kwargs)
        is_valid = form.is_valid()
        for formset in formsets.itervalues():
            is_valid = formset.is_valid() and is_valid
        if is_valid:
            obj = form.save()
            for formset in formsets.itervalues():
                formset.save()
            if self._mode == self.MODE_CREATE:
                msg = _("The {0} was created successfully.").format(self._model_verbose_name)
            else:
                msg = _("The {0} was updated successfully.").format(self._model_verbose_name)
            messages.success(request,msg,fail_silently=True)
            print form._errors
            return self.post_save_redirect(request,obj)
        else:
            print form._errors
            context = self.get_context(request,obj,form=form,**formsets)
            return self.response(request,context)

    def post_save_redirect(self,request,obj):
        return self.redirect(self._post_save_redirect,request,obj)

    def cancel_save_redirect(self,request):
        return self.redirect(self._cancel_save_redirect,request,None)

class ObjectCreateView(ObjectFormView):
    def __init__(self,form_class,post_save_redirect,cancel_save_redirect,
                 apply_to_form=None,form_prefix=None,formsets=None,**kwargs):
        ObjectFormView.__init__(self,form_class,post_save_redirect,cancel_save_redirect,
                                self.MODE_CREATE,apply_to_form,form_prefix,formsets,**kwargs)

class ObjectUpdateView(ObjectFormView):
    def __init__(self,form_class,post_save_redirect,cancel_save_redirect,
                 apply_to_form=None,form_prefix=None,formsets=None,**kwargs):
        ObjectFormView.__init__(self,form_class,post_save_redirect,cancel_save_redirect,
                                self.MODE_UPDATE,apply_to_form,form_prefix,formsets,**kwargs)

